#ifndef __PROTOCOL_H__
#define __PROTOCOL_H__

//服务器共享目录
#define FTP_DIR "/home/china/ftpboot"

//命令号
enum CMD_NO
{
	FTP_CMD_LS = 100,
	FTP_CMD_GET,
	FTP_CMD_PUT,
	FTP_CMD_BYE
};


/*
双方都需要遵守的数据格式:
客户端的请求命令格式
​	每一条命令都包括:
​	包头	包长度	命令号	参数1	参数2	...	包尾
包头	包长度	 命令号	 参数1	 参数2	...	包尾
参数:get和Put的时候,都需要说明文件的名字和文件的大小
    包头		占用1byte
                0xc0  所有的数据包的第一个字节都是0xc0,规定每一个数据包都以0xc0开头
                在实际应用中,为了保证包头的唯一性,一般会设置为多个字节
    包长度	 	占用4byte
                表示这个数据包的总长度(不包含包头和包尾)
                以小端模式存放这4个字节,先存放低字节
    命令号	 	占用4byte
                表示这个数据包的请求/应答功能
                以小端模式存放这4个字节,先存放低字节
    参数1
              len:	占用4byte 表示参数的数据长度,以小端模式存放这4个字节,先存放低字节
              data:  表示具体的数据(文件名....)
    参数2
              len:	占用4byte 表示参数的数据长度,以小端模式存放这4个字节,先存放低字节
              data:  表示具体的数据(文件名....)
    包尾		数据包的最后一个字节,规定每一个数据包都以0xc0结尾

例子:
客户端向服务器发送ls请求
包头(1)	包长度(4)	 命令号(4)  包尾(1)
char cmd[1024] = {0};  //表示我要发送的具体的数据序列
int pkg_len = 8;
enum CMD_NO  cmd_no = FTP_CMD_LS;
int i = 0;
cmd[i++] = 0xc0;   //包头

cmd[i++] = 0xff & pkg_len;   //存放pkg_len的第一个字节
cmd[i++] = 0xff & (pkg_len>>8);
cmd[i++] = 0xff & (pkg_len>>16);
cmd[i++] = 0xff & (pkg_len>>24);

cmd[i++] = 0xff & cmd_no;   //存放cmd_no的第一个字节
cmd[i++] = 0xff & (cmd_no>>8);
cmd[i++] = 0xff & (cmd_no>>16);
cmd[i++] = 0xff & (cmd_no>>24);

cmd[i++] = 0xc0;   //包尾

write(sockfd,cmd,i);
========================================
客户端向服务器发送get请求
cmd:get filename
包头(1)	包长度(4)	 命令号(4)  参数长度(4)  参数内容(文件名长度)  包尾(1)
char cmd[1024] = {0};  //表示我要发送的具体的数据序列
int arglen = strlen(filename);
int pkg_len = 4+4+4+arglen;
enum CMD_NO  cmd_no = FTP_CMD_GET;
int i = 0;
cmd[i++] = 0xc0;   //包头

cmd[i++] = 0xff & pkg_len;   //存放pkg_len的第一个字节
cmd[i++] = 0xff & (pkg_len>>8);
cmd[i++] = 0xff & (pkg_len>>16);
cmd[i++] = 0xff & (pkg_len>>24);

cmd[i++] = 0xff & cmd_no;   //存放cmd_no的第一个字节
cmd[i++] = 0xff & (cmd_no>>8);
cmd[i++] = 0xff & (cmd_no>>16);
cmd[i++] = 0xff & (cmd_no>>24);

cmd[i++] = 0xff & arglen;   //存放arglen的第一个字节
cmd[i++] = 0xff & (arglen>>8);
cmd[i++] = 0xff & (arglen>>16);
cmd[i++] = 0xff & (arglen>>24);

//strncpy(cmd+i,filename,arglen);
//i+=arglen;

int j = 0;
for(j=0;j<arglen;j++)
{
    cmd[i++] = filename[j];
}

cmd[i++] = 0xc0;   //包尾

write(sockfd,cmd,i);

========================================
客户端向服务器发送put请求
cmd:put filename
put 1.txt
包头(1)	包长度(4)	 命令号(4)  参数长度(4)  参数内容1(文件名内容)  参数内容2(4:文件内容大小)  包尾(1)
直接发送整个文件的内容

对于服务器来说:
收到0xc0包头之后,可以解析出这个数据包的长度,就可以获取到命令号put
解析参数长度,就可以知道文件名字的长度,就可以得到文件名(filename),还可以得到文件的内容大小(size)
再遇到0xc0之后的size个字节就是文件的具体内容
========================================
客户端向服务器发送bye请求
cmd:bye/byebye
包头(1)	包长度(4)	 命令号(4)  包尾(1)

总的来说,客户端需要按照上面的格式发送数据给服务器,服务器收到数据也需要严格的安装数据格式进行分解

*/

/*
服务器的回复命令格式
​	每一次回复的数据格式:
​	包头	包长度	命令号	回复长度   结果	回复内容	.... 包尾
包头(1)	包长度(4)	命令号(4)	回复长度(4)   结果(1)	 回复内容(根据命令不同而不同) 包尾(1)

    包头(1)		占用1byte
                0xc0  所有的数据包的第一个字节都是0xc0,规定每一个数据包都以0xc0开头
                在实际应用中,为了保证包头的唯一性,一般会设置为多个字节
    包长度(4)		占用4byte
                表示这个数据包的总长度(不包含包头和包尾)
                以小端模式存放这4个字节,先存放低字节
    命令号(4)	 	占用4byte
                表示这个数据包的请求/应答功能
                以小端模式存放这4个字节,先存放低字节
    回复长度(4)   4byte表示这个数据包的回复内容的长度
                以小端模式存放这4个字节,先存放低字节
    结果(1)	 	1byte 表示执行成功或者失败,0表示成功,1表示失败
    回复内容(根据命令不同而不同)
                成功
                    ls:所有文件的名字列表,每一个名字使用空格隔开
                    get:回复你要获取的文件大小size,发送完这个数据包之后,再把size个字节的文件内容发送出去
                    put:收到请求之后,就知道你要发生的文件名(filename)和文件大小(size)
                        直接把收到的size个字节的数据,写入到filename表示的文件中
                    bye:你都要断开连接了,作为服务器就直接关闭套接字,退出子进程

                失败
                    可以回复自定义的错误码(自定义的一些常见错误,用整数表示即可)
    包尾(1)  		数据包的最后一个字节,规定每一个数据包都以0xc0结尾

========================================================================
例子:进行客户端的ls请求的回复
    包头(1)	包长度(4)	命令号(4)	回复长度(4)   结果(1)	 回复内容(文件名列表) 包尾(1)
    //在回复ls前,需要提前准备好文件名列表filenames
char resp[4096] = {0};  //表示我要回复的具体的数据序列
int result = 0;  //获取文件名成功
int resplen = 1+strlen(filenames);
int pkg_len = 4+4+4+resplen;
enum CMD_NO  cmd_no = FTP_CMD_LS;
int i = 0;
resp[i++] = 0xc0;   //包头

resp[i++] = 0xff & pkg_len;   //存放pkg_len的第一个字节
resp[i++] = 0xff & (pkg_len>>8);
resp[i++] = 0xff & (pkg_len>>16);
resp[i++] = 0xff & (pkg_len>>24);

resp[i++] = 0xff & cmd_no;   //存放cmd_no的第一个字节
resp[i++] = 0xff & (cmd_no>>8);
resp[i++] = 0xff & (cmd_no>>16);
resp[i++] = 0xff & (cmd_no>>24);

resp[i++] = 0xff & resplen;   //存放resplen的第一个字节
resp[i++] = 0xff & (resplen>>8);
resp[i++] = 0xff & (resplen>>16);
resp[i++] = 0xff & (resplen>>24);

resp[i++] = result;
if(result == 1)  //失败就需要回复错误码
{
    resp[i++] = 0xff & errno;   //存放errno的第一个字节
    resp[i++] = 0xff & (errno>>8);
    resp[i++] = 0xff & (errno>>16);
    resp[i++] = 0xff & (errno>>24);
}else  //成功就需要回复文件名列表
{
    int j = 0;
    for(j=0;j<resplen-1;j++)
    {
        resp[i++] = filenames[j];
    }
}

resp[i++] = 0xc0;   //包尾

write(sockfd,resp,i);
========================================================================
进行客户端的get请求的回复
包头(1)	包长度(4)	命令号(4)	回复长度(4)   结果(1)	 回复内容(文件的长度) 包尾(1)
把包发送完成之后,再发送文件的size个字节的内容
*/


#endif